#include "CargadorFrases.h"

#include <fstream>
#include <locale>
#include <limits>
#include <cstdio>
#include <cstdlib>
#include <ctime>



CargadorFrases::CargadorFrases(HashFile& hf) : _hash(hf)
{
	std::srand( std::time(NULL) );
}


void CargadorFrases::cargarFrases(std::string nomArch){

	// Normaliza el archivo sacando los simbolos diacríticos para no
	// tener problemas con los "wide chars"
	std::string nomArchNormalizado = normalizarEntrada(nomArch);

	// Se procesa el archivo normalizado linea por linea
	std::ifstream fin(nomArchNormalizado.c_str(), std::ifstream::in);
	int cantLineas = 0;	// XXX: Limitador de lineas
	while (!fin.eof() && cantLineas < 8){
		std::string linea;
		std::getline(fin, linea);

		// No se porque, pero hay dos lineas que estan vacias, no las proceso
		if (!fin.eof() && linea != ""){

			// Extrae los datos de la linea y la convierte en un registro de dispersion
			RegistroHash rHash = parsearLinea(linea);
			this->_hash.insertarRegistro(rHash);
			++cantLineas;
		}
	}
	fin.close();

	// Eliminamos el archivo normalizado
	std::remove(nomArchNormalizado.c_str());
}


std::string CargadorFrases::normalizarEntrada(std::string nomArch) const{
	// Abre el archivo como wide char, el archivo debe ser UTF-8
	std::wifstream fin(nomArch.c_str());

	/** Se fuerza que el locale con el que trabajamos sea UTF-8,
	 * este deberia ser el valor por defecto, pero varia por PC
	 */
	std::locale loc("es_AR.UTF8");
	fin.imbue(loc);

	// Un archivo de salida con un nombre random
	char nomSalida[L_tmpnam] = {0};
	tmpnam(nomSalida);
	std::ofstream fout(nomSalida);

	while ( !fin.eof() ){

		std::wstring linea;
		std::getline(fin, linea);

		// Procesamos los caracteres uno a uno
		std::string s = "";
		for (unsigned i=0; i < linea.length(); ++i){

			// Se filtra el caracter y se lo agrega al final del string
			char c = filtrarChar(linea[i]);
			s.push_back(c);
		}

		// Se guarda la linea en el archivo de salida
		fout << s << std::endl;
	}

	fin.close();
	fout.close();

	return nomSalida;
}


char CargadorFrases::filtrarChar(wchar_t c) const{
	// Convierte el caracter en numero
	int ci = (int) c;

	// Los valores de los caracters permitidos
	int a = (int) 'a';
	int z = (int) 'z';
	int A = (int) 'A';
	int Z = (int) 'Z';

	// Los signos de puntuación permitidos
	int coma = (int) L',';
	int pipe = (int) L'|';
	int punto = (int) L'.';
	int espacio = (int) L' ';
	int comilla = (int) L'\'';
	int comillas = (int) L'"';
	int dos_puntos = (int) L':';
	int punto_y_coma = (int) L';';

	// Mas signos
	int pregunta_1 = (int) L'¿';
	int pregunta_2 = (int) L'?';
	int corchete_1 = (int) L'[';
	int corchete_2 = (int) L']';
	int exclamacion_1 = (int) L'¡';
	int exclamacion_2 = (int) L'!';


	// Compara que el caracter este dentro de los valores de las letras "normales"
	if ( (ci >= a && ci <= z) || (ci >= A && ci <=Z) ){

		return c;

	} else if (ci == coma || ci == pipe || ci == punto || ci == comillas || ci == comilla
			|| ci == dos_puntos || ci == punto_y_coma || ci == espacio){
		// Es un signo valido
		return c;

	} else if (ci == pregunta_1 || ci == pregunta_2 || ci == corchete_1 || ci == corchete_2
			|| ci == exclamacion_1 || ci == exclamacion_2){
		// Otro simbolo valido
		return c;

	} else {
		// Tiene un simbolo diacrítico, hay que aplicarle una transformacion
		return transformarChar(c);
	}
}


char CargadorFrases::transformarChar(wchar_t c) const{
	int cint = (int) c;
	switch (cint){
			case (int) L'á':
			case (int) L'ä':
				return 'a';

			case (int) L'Á':
			case (int) L'Ä':
				return 'A';

			case (int) L'é':
			case (int) L'ë':
				return 'e';

			case (int) L'É':
			case (int) L'Ë':
				return 'E';

			case (int) L'í':
			case (int) L'ï':
			case (int) L'î':
				return 'i';

			case (int) L'Í':
			case (int) L'Ï':
			case (int) L'Î':
				return 'I';

			case (int) L'ó':
			case (int) L'ö':
				return 'o';

			case (int) L'Ó':
			case (int) L'Ö':
				return 'O';

			case (int) L'ú':
			case (int) L'ü':
				return 'u';

			case (int) L'Ú':
			case (int) L'Ü':
				return 'U';

			case (int) L'ñ':
				return 'n';

			case (int) L'Ñ':
				return 'N';

			default:
				// Un caracter que no se que es
				return '#';
		}
}


// Cada linea es <apellido_autor>|<nombre_autor>|"<frase>"
RegistroHash CargadorFrases::parsearLinea(std::string linea) const{
	unsigned int divisor_A = linea.find('|');
	if (divisor_A == std::string::npos){
		throw std::runtime_error("La linea es invalida: " + linea);
	}

	unsigned int divisor_B = linea.find('|', divisor_A +1);
	if (divisor_B == std::string::npos){
		throw std::runtime_error("La linea es invalida: " + linea);
	}

	// Se separa la linea en sus componentes
	int clave = getRandom();
	std::string apellido = linea.substr(0, divisor_A);
	std::string nombre = linea.substr(divisor_A +1, divisor_B - divisor_A -1);
	std::string frase = linea.substr(divisor_B +1);

	// Se devuelve un registro
	RegistroHash reg (clave, apellido + ", " + nombre, frase);
	return reg;
}


unsigned long int CargadorFrases::getRandom() const{
	return std::rand() % std::numeric_limits<unsigned long int>::max();
}
